using System;
using System.Linq;
using LoggingMagic.AssemlyOperations;
using LoggingMagic.Injectors.Interfaces;
using LoggingMagic.Injectors.StaticField.Parameters;
using Mono.Cecil;
using LoggingMagic.Extensions;

namespace LoggingMagic.Injectors.StaticField.Base
{
    internal class StaticFieldMethodCallInjector : IInjector
    {
        protected readonly ModuleDefinition _module;
        protected readonly IStaticFieldParameters _parameters;

        public StaticFieldMethodCallInjector(ModuleDefinition module, IStaticFieldParameters parameters)
        {
            _module = module;
            _parameters = parameters;
        }



        public virtual void Inject(MethodDefinition method, Instructions ops)
        {
            if (SkipMethod(method))
                return;

            #region Log.Logger.Info("Entering")

            ops.Ldsfld(LoggingField);
            ops.Ldstr(EnteringText(method));
            ops.Call(LoggingMethod);

            #endregion
        }

        protected bool SkipMethod(MethodDefinition method)
        {
            if (method.IsConstructor)
            {
                return method.FullName.Contains(LoggingClass.FullName);
                //return true;
            }
            return false;
        }

        protected virtual string EnteringText(MethodDefinition method)
        {
            return "Entering";
        }

        #region Logging Method
        private MethodReference _loggingMethod;
        protected MethodReference LoggingMethod
        {
            get
            {
                if (_loggingMethod == null)
                {
                    _loggingMethod = FindLoggingMethod(LoggingField);
                }

                return _loggingMethod;
            }
        }

        protected virtual MethodReference FindLoggingMethod(FieldDefinition field)
        {
            var loggingMethod = LoggingField.FieldType.Resolve().Methods.Where(
                x => x.FullName.MethodSignature() == _parameters.LoggingMethodSignature).FirstOrDefault();

            if (loggingMethod == null)
                throw new InvalidOperationException(String.Format("LoggingMethod with signature {0} not found", _parameters.LoggingMethodSignature));

            return _module.Import(loggingMethod);
        }
        #endregion

        #region Logging Field
        private FieldDefinition _loggingField;
        protected FieldDefinition LoggingField
        {
            get
            {
                if (_loggingField == null)
                {
                    _loggingField = FindLoggingField();
                }

                return _loggingField;
            }
        }

        private TypeDefinition _loggingClass;
        protected TypeDefinition LoggingClass
        {
            get
            {
                if (_loggingClass == null)
                {
                    _loggingClass =
                        _module.Types.Where(x => x.Name == _parameters.LoggingClassName && x.IsClass).SingleOrDefault();

                    if (_loggingClass == null)
                        throw new InvalidOperationException(
                            String.Format(
                                "Class with the name '{0}' not found, or there's more than one class with that name",
                                _parameters.LoggingClassName));
                }

                return _loggingClass;
            }
        }

        protected virtual FieldDefinition FindLoggingField()
        {
            var field = LoggingClass.Fields.Where(x => x.IsStatic &&
                                                       x.FieldType.FullName == _parameters.LoggingFieldType).FirstOrDefault();

            if (field == null)
                throw new InvalidOperationException(String.Format("Field with the type '{0}' not found in class {1}", _parameters.LoggingFieldType, _parameters.LoggingClassName));


            return field;
        }
        #endregion


    }
}